#include <vector>

#include "texture/targa.h"

using std::vector;

namespace sheeplib {

    namespace texture {

        const GLubyte Targa::uTGAcompare[12] = {0,0, 2,0,0,0,0,0,0,0,0,0};
        const GLubyte Targa::cTGAcompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0};

        Targa::Targa() {

        }

        Targa::~Targa() {

        }

        bool Targa::LoadTextureFile(const string& filename) {

            ifstream fileIn(filename.c_str());

            if (!fileIn.is_open())
            {
                SetErrorString("The image file could not be opened: " + filename);
                return false;
            }

            fileIn.read(reinterpret_cast<char*>(&m_Header), sizeof(m_Header));
            fileIn.close();

            if(memcmp(uTGAcompare, &m_Header, sizeof(m_Header)) == 0) {
                LoadUncompressedTGA(filename);
            } else if (memcmp(cTGAcompare, &m_Header, sizeof(m_Header)) == 0) {
                LoadCompressedTGA(filename);
            } else {
                SetErrorString("The format of this image is not supported");
                return false;
            }

            return true;
        }

        bool Targa::ReadCommonHeader(ifstream& fileIn) {
            GLubyte header[6];

            fileIn.ignore(sizeof(GLubyte) * 12);
            fileIn.read(reinterpret_cast<char*>(&header), sizeof(header));

            GetFormat().m_Width = header[1] * 256 + header[0];
            GetFormat().m_Height = header[3] * 256 + header[2];
            GetFormat().m_BitsPerPixel	 = header[4];

            if (!GetFormat().m_Width || !GetFormat().m_Height) {
                SetErrorString("The image is invalid");
                return false;
            }

            if (GetFormat().m_BitsPerPixel != 24 && GetFormat().m_BitsPerPixel != 32) {
                SetErrorString("Only 32 bit and 24 bit targa images can be loaded");
                return false;
            }

            return true;
        }

        bool Targa::LoadCompressedTGA(const string& filename) {
            unsigned int bytesPerPixel;

            ifstream fileIn(filename.c_str(), std::ios::binary);

            if (!fileIn.is_open())
            {
                SetErrorString("The image file could not be opened: " + filename);
                return false;
            }

            if (!ReadCommonHeader(fileIn))
            {
                return false;
            }

            SetType((GetFormat().m_BitsPerPixel == 24)? GL_RGB : GL_RGBA);
            bytesPerPixel = (GetFormat().m_BitsPerPixel / 8);

            int imagesize = (bytesPerPixel * GetFormat().m_Width * GetFormat().m_Height);
            GetFormat().m_ImageData.resize(imagesize);

            GLuint pixelcount	= GetFormat().m_Height * GetFormat().m_Width;			// Number of pixels in the image
            GLuint currentpixel	= 0;												// Current pixel being read
            GLuint currentbyte	= 0;												// Current byte

			vector<GLubyte> colourbuffer(bytesPerPixel);

            do
            {
                GLubyte chunkheader = 0;

                fileIn.read(reinterpret_cast<char*>(&chunkheader), sizeof(GLubyte));

                if(chunkheader < 128) {
                    chunkheader++;

                    for(short counter = 0; counter < chunkheader; counter++) {
                        fileIn.read(reinterpret_cast<char*>(&colourbuffer[0]), bytesPerPixel);

                        GetFormat().m_ImageData[currentbyte] = colourbuffer[2];
                        GetFormat().m_ImageData[currentbyte + 1] = colourbuffer[1];
                        GetFormat().m_ImageData[currentbyte + 2] = colourbuffer[0];

                        if(bytesPerPixel == 4)
                            GetFormat().m_ImageData[currentbyte + 3] = colourbuffer[3];

                        currentbyte += bytesPerPixel;
                        currentpixel++;

                        if(currentpixel > pixelcount) {
                            fileIn.close();
                            return false;
                        }
                    }
                } else {
                    chunkheader -= 127;

                    fileIn.read(reinterpret_cast<char*>(&colourbuffer[0]), bytesPerPixel);

                    for(short counter = 0; counter < chunkheader; counter++) {
                        GetFormat().m_ImageData[currentbyte	] = colourbuffer[2];
                        GetFormat().m_ImageData[currentbyte + 1	] = colourbuffer[1];
                        GetFormat().m_ImageData[currentbyte + 2	] = colourbuffer[0];

                        if(bytesPerPixel == 4)
                            GetFormat().m_ImageData[currentbyte + 3] = colourbuffer[3];

                        currentbyte += bytesPerPixel;
                        currentpixel++;

                        if(currentpixel > pixelcount) {
                            fileIn.close();
                            return false;
                        }
                    }
                }
            } while(currentpixel < pixelcount);

            fileIn.close();

            return true;
        }

        bool Targa::LoadUncompressedTGA(const string& filename) {

            unsigned int bytesPerPixel;

            ifstream fileIn(filename.c_str(), std::ios::binary);

            if (!fileIn.is_open())
            {
                SetErrorString("The image file could not be opened: " + filename);
                return false;
            }

            if (!ReadCommonHeader(fileIn))
            {
                return false;
            }

            SetType( (GetFormat().m_BitsPerPixel == 24)? GL_RGB : GL_RGBA );
            bytesPerPixel = (GetFormat().m_BitsPerPixel / 8);

            unsigned int imagesize = (bytesPerPixel * GetFormat().m_Width * GetFormat().m_Height);
            GetFormat().m_ImageData.resize(imagesize);

            fileIn.read(reinterpret_cast<char*>(&GetFormat().m_ImageData[0]), imagesize);

            for(GLuint cswap = 0; cswap < imagesize; cswap += bytesPerPixel)
            {
                char swap = GetFormat().m_ImageData[cswap];
                GetFormat().m_ImageData[cswap] = GetFormat().m_ImageData[cswap+2];
                GetFormat().m_ImageData[cswap+2] = swap;
            }

            fileIn.close();

            return true;
        }
    }


}
